package luke.audio.processor;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.FrameRecorder;
import org.bytedeco.javacv.FFmpegLogCallback;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.nio.file.Files;
import luke.core.logging.LogUtil;

/**
 * 音频转码器（静态方法类）
 * 负责WAV与PCM数据之间的相互转换，以及多种格式转为WAV
 */
public class AudioTranscoder {
    
    // 音频参数
    private static final int SAMPLE_RATE = 44100; // 采样率
    private static final int CHANNELS = 2;        // 声道数（立体声）
    private static final int BYTES_PER_SAMPLE = 2; // 每个样本的字节数（16位）

    /**
     * 获取采样率
     *
     * @return 采样率
     */
    public static int getSampleRate() {
        return SAMPLE_RATE;
    }

    /**
     * 获取声道数
     *
     * @return 声道数
     */
    public static int getChannels() {
        return CHANNELS;
    }

    /**
     * 获取每个样本的字节数
     *
     * @return 每个样本的字节数
     */
    public static int getBytesPerSample() {
        return BYTES_PER_SAMPLE;
    }

    /**
     * 从文件读取字节数据
     *
     * @param file 音频文件
     * @return 文件的字节数据
     * @throws IOException 如果读取文件时发生错误
     */
    public static byte[] readFileToBytes(File file) throws IOException {
        if (file == null || !file.exists()) {
            throw new IOException("文件不存在或为空");
        }
        
        return Files.readAllBytes(file.toPath());
    }

    /**
     * 从WAV数据中提取PCM数据
     *
     * @param wavData WAV音频数据（包含文件头）
     * @return PCM音频数据
     */
    public static byte[] extractPCMFromWAV(byte[] wavData) {
        if (wavData == null || wavData.length == 0) {
            return new byte[0];
        }

        // 解析WAV文件头以获取PCM数据的起始位置
        int dataOffset = findDataChunkOffset(wavData);
        if (dataOffset < 0 || dataOffset >= wavData.length) {
            return new byte[0];
        }

        // 获取PCM数据
        byte[] pcmData = new byte[wavData.length - dataOffset];
        System.arraycopy(wavData, dataOffset, pcmData, 0, pcmData.length);
        return pcmData;
    }

    /**
     * 将wav的字节数据写成wav文件
     *
     * @param wavData WAV音频数据
     * @param filePath 目标文件路径
     * @throws IOException 如果写入文件时发生错误
     */
    public static void writeBytesToFile(byte[] wavData, File filePath) throws IOException {
        if (wavData == null || filePath == null) {
            throw new IOException("数据或文件路径为空");
        }
        
        // 确保父目录存在
        File parentDir = filePath.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            parentDir.mkdirs();
        }
        
        // 写入文件
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            fos.write(wavData);
        }
    }

    /**
     * 将PCM数据包装成WAV格式数据（使用JavaCV的方法）
     *
     * @param pcmData PCM音频数据
     * @return WAV格式音频数据
     * @throws IOException 如果处理过程中发生IO错误
     */
    public static byte[] wrapPCMAsWAV(byte[] pcmData) throws IOException {
        if (pcmData == null || pcmData.length == 0) {
            return new byte[0];
        }

        // 确保PCM数据长度是帧对齐的（立体声16位PCM每帧4字节）
        int frameSize = CHANNELS * BYTES_PER_SAMPLE; // 2声道 * 2字节 = 4字节每帧
        if (pcmData.length % frameSize != 0) {
            // 如果长度不是帧对齐的，截断到最近的帧边界
            int alignedLength = (pcmData.length / frameSize) * frameSize;
            if (alignedLength <= 0) {
                return new byte[0]; // 如果没有完整的帧，返回空数组
            }
            byte[] alignedPcmData = new byte[alignedLength];
            System.arraycopy(pcmData, 0, alignedPcmData, 0, alignedLength);
            pcmData = alignedPcmData;
        }

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            // 创建录制器
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, CHANNELS);
            recorder.setSampleRate(SAMPLE_RATE);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE);
            recorder.setSampleFormat(avutil.AV_SAMPLE_FMT_S16);
            recorder.setFormat("wav");
            
            // 关键：显式禁用视频流
            recorder.setVideoBitrate(0);
            recorder.setFrameRate(0);
            
            // 启动录制器
            recorder.start();

            // 将PCM数据转换为ShortBuffer以便使用recordSamples方法
            // 确保ByteBuffer是直接缓冲区且正确对齐
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(pcmData.length);
            byteBuffer.put(pcmData);
            byteBuffer.flip(); // 准备读取
            ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
            
            // 使用recordSamples方法将PCM数据写入WAV文件
            recorder.recordSamples(SAMPLE_RATE, CHANNELS, shortBuffer);

            // 停止录制器
            recorder.stop();
            
            // 返回WAV数据
            return outputStream.toByteArray();
        } catch (FrameRecorder.Exception e) {
            throw new IOException("转换PCM到WAV时发生错误", e);
        } catch (Exception e) {
            throw new IOException("转换PCM到WAV时发生未知错误", e);
        }
    }

    /**
     * 将文件转换为WAV字节数据（自动识别格式）
     *
     * @param file 音频文件
     * @return WAV格式音频数据
     * @throws IOException 如果处理过程中发生IO错误
     */
    public static byte[] convertFileToWAV(File file) throws IOException {
        if (file == null || !file.exists()) {
            throw new IOException("文件不存在或为空");
        }

        // 首先检查并处理抖音视频结尾音
        file = checkAndRemoveDouyinEndingSound(file);

        // 读取文件数据
        byte[] fileData = readFileToBytes(file);
        
        // 自动识别文件格式
        String fileName = file.getName().toLowerCase();
        String format = "wav"; // 默认格式
        
        if (fileName.endsWith(".mp3")) {
            format = "mp3";
        } else if (fileName.endsWith(".flac")) {
            format = "flac";
        } else if (fileName.endsWith(".aac")) {
            format = "aac";
        } else if (fileName.endsWith(".wma")) {
            format = "wma";
        } else if (fileName.endsWith(".ogg")) {
            format = "ogg";
        } else if (fileName.endsWith(".amr")) {
            format = "amr";
        } else if (fileName.endsWith(".ape")) {
            format = "ape";
        } else if (fileName.endsWith(".alac")) {
            format = "alac";
        } else if (fileName.endsWith(".m4a")) {
            format = "m4a";
        } else if (fileName.endsWith(".ac3")) {
            format = "ac3";
        } else if (fileName.endsWith(".mp4")) {
            format = "mp4";
        } else if (fileName.endsWith(".mov")) {
            format = "mov";
        } else if (fileName.endsWith(".avi")) {
            format = "avi";
        } else if (fileName.endsWith(".wmv")) {
            format = "wmv";
        } else if (fileName.endsWith(".mkv")) {
            format = "mkv";
        } else if (fileName.endsWith(".mpeg")) {
            format = "mpeg";
        }

        return convertAudioToWAV(fileData, format);
    }

    /**
     * 将文件转换为PCM字节数据（自动识别格式）
     *
     * @param file 音频文件
     * @return PCM格式音频数据
     * @throws IOException 如果处理过程中发生IO错误
     */
    public static byte[] convertFileToPCM(File file) throws IOException {
        // 先转换为WAV
        byte[] wavData = convertFileToWAV(file);
        
        // 从WAV中提取PCM数据
        return extractPCMFromWAV(wavData);
    }

    /**
     * 通用音频格式转WAV方法
     *
     * @param audioData 音频数据
     * @param format 音频格式
     * @return WAV格式音频数据
     * @throws IOException 如果处理过程中发生IO错误
     */
    private static byte[] convertAudioToWAV(byte[] audioData, String format) throws IOException {
        if (audioData == null || audioData.length == 0) {
            return new byte[0];
        }

        // 设置FFmpeg日志回调以获取详细错误信息
        FFmpegLogCallback.set();

        ByteArrayInputStream inputStream = new ByteArrayInputStream(audioData);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        FFmpegFrameGrabber grabber = null;
        FFmpegFrameRecorder recorder = null;

        try {
            // 配置并启动抓取器
            grabber = new FFmpegFrameGrabber(inputStream);
            grabber.setFormat(format);
            grabber.start();

            // 获取音频参数
            int audioChannels = grabber.getAudioChannels();
            int sampleRate = grabber.getSampleRate();
            
            // 强制使用统一的音频参数以确保输出一致性
            audioChannels = CHANNELS;
            sampleRate = SAMPLE_RATE;

            // 创建录制器 - 使用更简单的构造函数
            recorder = new FFmpegFrameRecorder(outputStream, audioChannels);
            recorder.setSampleRate(sampleRate);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE);
            recorder.setSampleFormat(avutil.AV_SAMPLE_FMT_S16);
            recorder.setFormat("wav");
            
            // 关键：显式禁用视频流
            recorder.setVideoBitrate(0);
            recorder.setFrameRate(0);
            
            // 启动录制器
            recorder.start();

            // 逐帧读取并写入
            Frame frame;
            while ((frame = grabber.grab()) != null) {
                // 只处理音频帧
                if (frame.samples != null) {
                    recorder.record(frame);
                }
            }

            // 停止录制器
            recorder.stop();
            
            // 返回WAV数据
            return outputStream.toByteArray();
            
        } catch (FrameGrabber.Exception | FrameRecorder.Exception e) {
            throw new IOException("转换" + format.toUpperCase() + "到WAV时发生错误", e);
        } finally {
            // 确保资源被释放
            try {
                if (recorder != null) {
                    recorder.release();
                }
                if (grabber != null) {
                    grabber.release();
                }
                inputStream.close();
                outputStream.close();
            } catch (FrameRecorder.Exception | FrameGrabber.Exception e) {
                // 忽略释放资源时的异常
                System.err.println("释放资源时发生警告: " + e.getMessage());
            }
        }
    }

        /**
     * 检查并处理抖音视频结尾音
     * 在音频转码前调用此方法
     *
     * @param file 输入文件
     * @return 处理后的文件
     */
    public static File checkAndRemoveDouyinEndingSound(File file) {
        try {
            return luke.audio.processor.DouyinEndingSoundRemover.checkAndRemoveEndingSound(file);
        } catch (Exception e) {
            LogUtil.error("AudioTranscoder", "检查抖音结尾音", "处理文件时发生错误: " + e.getMessage());
            return file;
        }
    }

    /**
     * 查找WAV文件中data块的偏移量
     *
     * @param wavData WAV数据
     * @return data块的偏移量，如果未找到则返回-1
     */
    private static int findDataChunkOffset(byte[] wavData) {
        // WAV文件头至少需要44字节
        if (wavData.length < 44) {
            return -1;
        }

        // 查找"data"标识符
        for (int i = 36; i <= wavData.length - 4; i++) {
            if (wavData[i] == 'd' && wavData[i+1] == 'a' && wavData[i+2] == 't' && wavData[i+3] == 'a') {
                // 返回data块的起始位置（包括"data"标识符和数据大小字段）
                int offset = i + 8;
                return offset;
            }
        }

        return -1;
    }
}